package com.haohan.jingyuan.util;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 通用缓存工具类。
 * <p>
 * 提供本地缓存功能，支持多种类型的缓存操作
 * </p>
 */
@Slf4j
@Component
public class CacheUtil {

    /**
     * 缓存的Key
     */
    private final ConcurrentHashMap<String, String> cacheKeys = new ConcurrentHashMap<>();
    /**
     * 自定义缓存实例集合，支持不同的过期时间
     */
    private final ConcurrentHashMap<String, Cache<String, Object>> cacheWarehouse = new ConcurrentHashMap<>();

    /**
     * 存储数据到缓存，支持自定义过期时间。
     *
     * @param key      缓存键
     * @param value    缓存值
     * @param duration 过期时间
     * @param timeUnit 时间单位
     */
    public void put(String key, Object value, long duration, TimeUnit timeUnit) {
        String cache = routeCache(duration, timeUnit);

        // 第一次设置缓存数据
        if (cacheKeys.get(key) == null) {
            cacheKeys.put(key, cache);
            if (cacheWarehouse.get(cache) == null) {
                initCache(cache, duration, timeUnit);
            }
            cacheWarehouse.get(cache).put(key, value);
            return;
        }

        // 非第一次设置缓存数据，缓存仓库一致，做update操作
        if (cache.equals(cacheKeys.get(key))) {
            cacheWarehouse.get(cache).put(key, value);
        }
        // 缓存仓库不一致，init仓库后新增
        else {
            if (cacheKeys.get(key) == null) {
                cacheKeys.put(key, cache);
            }
            if (cacheWarehouse.get(cache) == null) {
                initCache(cache, duration, timeUnit);
            }
            cacheWarehouse.get(cache).put(key, value);
            cacheWarehouse.get(cacheKeys.get(key)).invalidate(key);
        }
        cacheKeys.put(key, cache);
    }

    /**
     * 默认设置缓存，时长1小时
     *
     * @param key
     * @param value
     */
    public void put(String key, Object value) {
        put(key, value, 1, TimeUnit.DAYS);
    }

    /**
     * 从自定义缓存中获取数据。
     *
     * @param key 缓存键
     * @return 缓存值，如果不存在则返回null
     */
    public Object get(String key) {
        String ck = cacheKeys.get(key);
        if (ck == null) {
            return null;
        }
        Object ifPresent = cacheWarehouse.get(ck).getIfPresent(key);
        if (ifPresent == null) {
            cacheKeys.remove(key);
        }
        return ifPresent;
    }

    /**
     * 从自定义缓存中获取指定类型的数据。
     *
     * @param key   缓存键
     * @param clazz 期望的数据类型
     * @param <T>   泛型类型
     * @return 缓存值，如果不存在或类型不匹配则返回null
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        Object value = get(key);
        if (value != null && clazz.isInstance(value)) {
            return (T) value;
        }
        return null;
    }

    /**
     * 从自定义缓存中移除数据。
     *
     * @param key 缓存键
     */
    public void remove(String key) {
        String ck = cacheKeys.get(key);
        if (ck == null) {
            return;
        }
        cacheWarehouse.get(ck).invalidate(key);
        cacheKeys.remove(key);
    }

    /**
     * 路由到对应时长的缓存仓库。
     *
     * @param duration 过期时间
     * @param timeUnit 时间单位
     * @return 缓存键
     */
    private String routeCache(long duration, TimeUnit timeUnit) {
        return "cache_" + duration + "_" + timeUnit.name();
    }

    /**
     * 获取或创建自定义缓存实例。
     *
     * @param cacheKey 缓存键
     * @param duration 过期时间
     * @param timeUnit 时间单位
     * @return 缓存实例
     */
    private Cache<String, Object> initCache(String cacheKey, long duration, TimeUnit timeUnit) {
        return cacheWarehouse.computeIfAbsent(cacheKey, k -> {
            return Caffeine.newBuilder()
                    .maximumSize(1000)
                    .expireAfterWrite(duration, timeUnit)
                    .expireAfterAccess(duration, timeUnit)
                    .recordStats()
                    .build();
        });
    }
}
